package com.ruoyi.optimization.common;

import java.util.*;

public class MaterialOptimization {

    public static List<MaterialReq> materials = new ArrayList<>();
    public static List<PartsReq> parts = new ArrayList<>();

    public static void main(String[] args) {
        ArrayList<MaterialReq> materialReq = new ArrayList<>();
        materialReq.add(new MaterialReq(6000,55));
        materialReq.add(new MaterialReq(5400,55));
        materialReq.add(new MaterialReq(6400,55));
        ArrayList<PartsReq> partsReq = new ArrayList<>();
        partsReq.add(new PartsReq("A",854, 53));
        partsReq.add(new PartsReq("B",1423, 30));
        partsReq.add(new PartsReq("C",1356, 22));
        partsReq.add(new PartsReq("D",592, 33));
        long start = System.currentTimeMillis();
        Individual individual = MaterialOptimization.geneticAlgorithm(materialReq, partsReq);
        long end = System.currentTimeMillis();
        System.out.println("耗时: " + (end-start) + "利用率："+ individual.getFitness());
        if (individual != null){
            individual.getMaterialMap().forEach((key, value) -> {
                System.out.println("key: " + key + " 使用数量: " + value.size());
                value.forEach(element1 -> {
                    System.out.println("partLength: " + element1.getLength() + " parts: " + element1.getParts() + "remaing:"+element1.getRemaining());
                });
            });
        }
//        System.out.println(individual.getFitness());
//        System.out.println(JSON.toJSONString(individual));
    }
    // 遗传算法主函数（核心算法流程）
    public static Individual geneticAlgorithm(List<MaterialReq> materials1, List<PartsReq> parts1) {
        materials = materials1;
        parts = parts1;
        
        // 初始化种群（个体集合）
        List<Individual> population = new ArrayList<>();
        for (int i = 0; i < FinalNum.POPULATION_SIZE; i++) {
            population.add(new Individual(materials1, parts1));
        }
        for (Individual element : population) {
            if (!element.isValid()) {
                return null;
            }
        }
        List<Integer> materialArr = new ArrayList<>();
        for (MaterialReq element : materials) {
            materialArr.add(element.getLength());
        }
        materialArr.sort(Comparator.naturalOrder());
        System.out.println(materialArr);

        // 初始化最优解
        Individual bestSolution = null;
        // 迭代进化过程
        for (int gen = 0; gen < FinalNum.GENERATIONS; gen++) {
            // 计算适应度
            for (Individual ind : population) {
                ind.calculateFitness();
            }

            // 适应度排序（降序）
            population.sort((a, b) -> Double.compare(b.getFitness(), a.getFitness()));
            // 更新最优解（适应度最高）
            bestSolution = population.get(0).clone();
            // 搜索材料中可以用短一些的材料切割的方案
//            for (Map.Entry<Integer, List<Material>> entry : population.get(0).getMaterialMap().entrySet()) {
//                List<Material> instances = entry.getValue();
//                for (int i = instances.size() - 1; i >= 0; i--) {
//                    int differ = instances.get(i).getLength() - instances.get(i).getRemaining();
//                    for (int j = 0; j < materialArr.size(); j++) {
//                        if (differ <= materialArr.get(j)) {
//                            Integer integer = population.get(0).getMaterialCounts().get(materialArr.get(j)) - population.get(0).getMaterialMap().get(population.get(0).getMaterialLengthIndex().get(materialArr.get(j))).size();
//                            if (materialArr.get(j) != instances.get(i).getLength() && integer > 0) {
//                                Material newIn = instances.get(i).clone();
//                                newIn.setLength(materialArr.get(j));
//                                // 当前剩余可用长度
//                                newIn.setRemaining(newIn.getLength() - (instances.get(i).getLength() - instances.get(i).getRemaining()));
//                                population.get(0).getMaterialMap().get(population.get(0).getMaterialLengthIndex().get(newIn.getLength())).add(newIn);
//                                instances.remove(i);
//                            }
//                            break;
//                        }
//                    }
//                }
//            }
            population.get(0).calculateFitness();

            // 选择操作（精英保留策略）
            List<Individual> elites = population.subList(0, (int) (FinalNum.POPULATION_SIZE * FinalNum.ELITISM_RATE));

            // 复制精英个体，避免丢失优良解
            List<Individual> newPopulation = new ArrayList<>();
            for (Individual e : elites) {
                newPopulation.add(e.clone());
            }

            // 生成新种群（交叉与变异）
            while (newPopulation.size() < FinalNum.POPULATION_SIZE) {
                // 父代选择
                Individual parentA = selectParent(population);
                Individual parentB = selectParent(population);
                // 交叉操作（增强型，避免克隆错误）
                Individual child = constrainedCrossover(parentA, parentB);
                // 变异操作（约束型，修复克隆错误）
                constrainedMutate(child);
                // 加入新种群
                newPopulation.add(child);
            }

            localSearch(elites);
            population = newPopulation;
        }

        return bestSolution;
    }

    // 选择操作（锦标赛赛制）
    private static Individual selectParent(List<Individual> population) {
        // 锦标赛赛制参数设置
        int tournamentSize = 5;
        // 随机选择候选个体（随机锦标赛）
        List<Individual> candidates = new ArrayList<>();
        for (int i = 0; i < tournamentSize; i++) {
            candidates.add(population.get((int) (Math.random() * population.size())));
        }
        // 锦标赛赛制选出最优者
        candidates.sort((a, b) -> Double.compare(b.getFitness(), a.getFitness()));
        return candidates.get(0).clone();
    }

    // 增强型交叉操作（修复克隆错误的核心）
    private static Individual constrainedCrossover(Individual parentA, Individual parentB) {
        Individual child = parentA.clone();  // 创建父A的克隆体
        List<Material> materialArr = new ArrayList<>();
        for (List<Material> instancesA : parentA.getMaterialMap().values()) {
            materialArr.addAll(instancesA);
        }
        for (List<Material> instancesA : parentB.getMaterialMap().values()) {
            materialArr.addAll(instancesA);
        }
        materialArr.sort(Comparator.comparingInt(Material::getRemaining));
        Integer[] array = new Integer[parentA.getParts().size()];
        List<Integer> list = new ArrayList<>();
        for (PartsReq partsReq : parentA.getParts()) {
            Integer count = partsReq.getCount();
            list.add(count);
        }
        list.toArray(array);

        child.setPartsUsed(array); // 零件剩余计数器
        child.getMaterialMap().clear();
        for (int i = 0; i < materials.size(); i++) {
            child.getMaterialMap().put(i, new ArrayList<>());
        }
        for (int i = 0; i < materialArr.size(); i++) {
            boolean is = true;
            Integer[] tmp = Arrays.copyOf(child.getPartsUsed(), child.getPartsUsed().length);
            for (int j = 0; j < materialArr.get(i).getParts().size(); j++) {
                if (tmp[child.getPartsLengthIndex().get(materialArr.get(i).getParts().get(j))] > 0) {
                    tmp[child.getPartsLengthIndex().get(materialArr.get(i).getParts().get(j))]--;
                } else {
                    is = false;
                    break;
                }
            }
            int num = child.getMaterialCounts().get(materialArr.get(i).getLength())-child.getMaterialMap().get(child.getMaterialLengthIndex().get(materialArr.get(i).getLength())).size();
            if (is &&  num>0) {
                child.setPartsUsed(Arrays.copyOf(tmp, tmp.length));
                child.getMaterialMap().get(child.getMaterialLengthIndex().get(materialArr.get(i).getLength())).add(materialArr.get(i));
            }
        }

        List<Integer> missingParts = new ArrayList<>();
        for (int i = 0; i < child.getPartsUsed().length; i++) {
            for (int j = 0; j < child.getPartsUsed()[i]; j++) {
                missingParts.add(i);
            }
        }

        // 尝试重新分配缺失的零件
        if (!missingParts.isEmpty()) {
            class Backtrack {
                boolean backtrack(int index) {
                    if (index == missingParts.size()) return true;
                    int partType = missingParts.get(index);
                    int partLen = parts.get(partType).getLength();

                    // 尝试放入现有原料实例
                    for (Map.Entry<Integer, List<Material>> entry : child.getMaterialMap().entrySet()) {
                        List<Material> instances = entry.getValue();
                        for (Material inst : instances) {
                            if (inst.getRemaining() >= partLen) {
                                inst.getParts().add(partLen);
                                inst.setRemaining(inst.getRemaining()-partLen);
                                if (backtrack(index + 1)) return true;
                                inst.getParts().remove(inst.getParts().size() - 1);
                                inst.setRemaining(inst.getRemaining()+partLen);
                            }
                        }
                    }

                    // 尝试创建新原料实例
                    for (int matIdx = 0; matIdx < materials.size(); matIdx++) {
                        MaterialReq mat = materials.get(matIdx);
                        List<Material> instances = child.getMaterialMap().get(matIdx);
                        if (instances.size() < mat.getCount()) {
                            Material newInst = new Material(mat.getLength(), 1);
                            if (newInst.addPart(partLen)) {
                                instances.add(newInst);
                                if (backtrack(index + 1)) return true;
                                instances.remove(instances.size() - 1);
                            }
                        }
                    }

                    return false;
                }
            }
            Backtrack backtrack = new Backtrack();
            backtrack.backtrack(0);
        }

        child.calculateFitness();
        return child;
    }

    // 约束型变异操作
    private static void constrainedMutate(Individual individual) {
        // 选择余料最大的实例
        List<Material> candidates = new ArrayList<>();
        for (List<Material> instances : individual.getMaterialMap().values()) {
            candidates.addAll(instances);
        }
        candidates.sort((a, b) -> Integer.compare(b.getRemaining(), a.getRemaining()));

        if (!candidates.isEmpty()) {
            Material targetInst = candidates.get(0);
            if (!targetInst.getParts().isEmpty()) {
                // 取出最后一个零件
                int partLen = targetInst.getParts().remove(targetInst.getParts().size() - 1);
                targetInst.setRemaining(targetInst.getRemaining()+partLen);

                // 重新分配策略
                boolean relocated = false;
                for (List<Material> instances : individual.getMaterialMap().values()) {
                    for (Material inst : instances) {
                        if (!relocated && inst.getRemaining() >= partLen) {
                            inst.getParts().add(partLen);
                            inst.setRemaining(inst.getRemaining()-partLen);
                            relocated = true;
                        }
                    }
                }

                // 回滚机制（分配失败时）
                if (!relocated) {
                    targetInst.getParts().add(partLen);
                    targetInst.setRemaining(targetInst.getRemaining()-partLen);
                }
            }
        }
    }

    // 局部搜索（邻域优化）
    private static void localSearch(List<Individual> elites) {
        for (Individual ind : elites) {
            Individual neighbor = swapNeighbor(ind.clone());
            if (neighbor.getFitness() > ind.getFitness()) {
                ind.setMaterialMap(neighbor.getMaterialMap());
            }
        }
    }

    // 邻域搜索（零件交换）
    private static Individual swapNeighbor(Individual individual) {
        boolean improved = false;
        for (List<Material> instances : individual.getMaterialMap().values()) {
            for (Material inst : instances) {
                if (inst.getParts().size() >= 2) {
                    List<Integer> temp = new ArrayList<>(inst.getParts());
                    Collections.swap(temp, 0, 1);
                    int newRemain = inst.getLength() - temp.stream().mapToInt(Integer::intValue).sum();
                    if (newRemain < inst.getRemaining()) {
                        inst.getParts().clear();
                        inst.getParts().addAll(temp);
                        inst.setRemaining(newRemain);
                        improved = true;
                    }
                }
            }
        }
        if (improved) individual.calculateFitness();
        return individual;
    }
}
